package com.lj.framework.aspectj.datascope;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.lj.common.annotation.datascope.DataScope;
import com.lj.common.core.model.BaseModel;
import com.lj.common.core.model.system.SysRole;
import com.lj.common.core.model.system.SysUser;
import com.lj.common.core.model.vo.DataScopeParam;
import com.lj.common.core.model.vo.LoginUser;
import com.lj.common.enums.datascope.DataScopeTypeEnum;
import com.lj.common.enums.datascope.DataScopeVersionEnum;
import com.lj.common.utils.security.SecurityUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.HexValue;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.stream.Collectors;

/**
 * 数据过滤处理
 *
 * @author 小李飞刀
 */
@Slf4j
@Aspect
@Component
public class DataScopeAspect implements DataPermissionHandler {

    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";
    /**
     * 通过ThreadLocal记录权限相关的属性值
     */
    ThreadLocal<DataScopeParam> threadLocal = new ThreadLocal<>();
    /**
     * 注解对象
     */
    private DataScope dataScope;

    /**
     * Mybatis 版本数据范围过滤
     *
     * @param joinPoint 切点
     * @param user      用户
     * @param deptAlias 别名
     * @param userAlias 别名
     */
    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) {
        StringBuilder sqlString = new StringBuilder();

        for (SysRole role : user.getRoles()) {
            String dataScope = role.getDataScope();
            if (CharSequenceUtil.equals(DataScopeTypeEnum.ALL.getType(), dataScope)) {
                sqlString = new StringBuilder();
                break;
            } else if (CharSequenceUtil.equals(DataScopeTypeEnum.CUSTOM.getType(), dataScope)) {
                sqlString.append(CharSequenceUtil.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
            } else if (CharSequenceUtil.equals(DataScopeTypeEnum.OWN_DEPT.getType(), dataScope)) {
                sqlString.append(CharSequenceUtil.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
            } else if (CharSequenceUtil.equals(DataScopeTypeEnum.OWN_AND_CHILD_DEPT.getType(), dataScope)) {
                sqlString.append(CharSequenceUtil.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
            } else if (CharSequenceUtil.equals(DataScopeTypeEnum.OWN_ONLY.getType(), dataScope)) {
                if (CharSequenceUtil.isNotBlank(userAlias)) {
                    sqlString.append(CharSequenceUtil.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
                } else {
                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
                    sqlString.append(" OR 1=0 ");
                }
            }
        }

        if (CharSequenceUtil.isNotBlank(sqlString.toString())) {
            BaseModel baseEntity = (BaseModel) joinPoint.getArgs()[0];
            baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
        }
    }

    /**
     * 配置织入点
     */
    @Pointcut("@annotation(com.lj.common.annotation.datascope.DataScope)")
    public void dataScopePointCut() {
    }

    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) {
        // 清除之前的数据权限范围
        clearDataScope(point);
        // 获得注解
        dataScope = getAnnotationLog(point);
        if (dataScope != null) {
            // 获取当前的用户及相关属性，需提前获取和保存数据权限对应的部门ID集合
            LoginUser currentUser = SecurityUtil.getLoginUser();
            // 拼接数据权限所带参数
            DataScopeParam dataScopeParam = new DataScopeParam(
                    dataScope.version(),
                    dataScope.userAlias(),
                    dataScope.deptAlias(),
                    dataScope.userField(),
                    dataScope.deptField(),
                    currentUser.getUser().isAdmin(),
                    currentUser.getDeptIds());
            threadLocal.set(dataScopeParam);
            log.debug("currentUser.getDeptIds() = {}", currentUser.getDeptIds());
            // 处理 BASE_MODEL 带有参数版本的数据权限
            if (dataScope.version() == DataScopeVersionEnum.PARAMETER) {
                // 如果不是超管做一下操作否则不处理
                if (!currentUser.getUser().isAdmin()) {
                    // Mybatis 版本数据权限
                    dataScopeFilter(point, currentUser.getUser(), dataScope.deptAlias(), dataScope.userAlias());
                }
            }

        }
    }

    /**
     * 拼接权限sql前先清空params.dataScope参数防止注入
     */
    private void clearDataScope(final JoinPoint joinPoint) {
        Object params = joinPoint.getArgs()[0];
        if (ObjectUtil.isNotNull(params) && params instanceof BaseModel) {
            BaseModel baseModel = (BaseModel) params;
            baseModel.getParams().put(DATA_SCOPE, "");
        }
    }

    /**
     * 处理 BASE_MODEL 没有参数的版本
     *
     * @param where             原SQL Where 条件表达式
     * @param mappedStatementId Mapper接口方法ID
     * @return 结果
     */
    @SneakyThrows
    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        log.debug("DataScopePermissionHandler.getSqlSegment");
        DataScopeParam dataScopeParam = threadLocal.get();

        if (dataScope == null || dataScopeParam == null || dataScopeParam.isAdmin()) {
            return where;
        }
        // 这里只处理 BASE_MODEL 没有参数的版本
        if (DataScopeVersionEnum.NO_PARAMETER != dataScopeParam.getVersion()) {
            return where;
        }
        if (where == null) {
            where = new HexValue(" 1 = 1 ");
        }

        String deptSql = "".equals(dataScopeParam.getDeptAlias()) ? dataScopeParam.getDeptField() : dataScopeParam.getDeptAlias() + "." + dataScopeParam.getDeptField();
        String userSql = "".equals(dataScopeParam.getUserAlias()) ? dataScopeParam.getUserField() : dataScopeParam.getUserAlias() + "." + dataScopeParam.getUserField();
        // 把集合转变为JSQLParser需要的元素列表
        ItemsList itemsList;
        // 数据权限为空有两种情况-1. 用户没有配置角色 | 2. 带有全部数据权限的角色
        if (CollUtil.isEmpty(dataScopeParam.getSecretary())) {
            if (SecurityUtil.getLoginUser().getUser().getRoles().stream().map(SysRole::getDataScope).collect(Collectors.toSet()).contains(DataScopeTypeEnum.ALL.getType())) {
                // 如果角色中带有数据权限为所有的角色 - 直接返回
                return where;
            } else {
                //如果权限为空，则只能看自己部门 | 或者数据权限为自己的
                itemsList = new ExpressionList(Collections.singletonList(new LongValue(SecurityUtil.getLoginUser().getUser().getDeptId())));
            }
        } else {
            // 查看权限set的大小只有 1 且 内容为 -1
            if (dataScopeParam.getSecretary().size() == 1 && dataScopeParam.getSecretary().contains(-1L)) {
                EqualsTo equalsToExpression = new EqualsTo(new Column(userSql), new LongValue(SecurityUtil.getLoginUser().getUser().getUserId()));
                return new AndExpression(where, equalsToExpression);
            } else {
                // 其他正常查询数据权限内的数据
                itemsList = new ExpressionList(dataScopeParam.getSecretary().stream().map(LongValue::new).collect(Collectors.toList()));
            }
        }
        InExpression inExpression = new InExpression(new Column(deptSql), itemsList);
        log.debug("where = {}", where);
        log.debug("inExpression = {}", inExpression);
        return new AndExpression(where, inExpression);

    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private DataScope getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(DataScope.class);
        }
        return null;
    }


    /**
     * 清空当前线程上次保存的权限信息
     */
    @After("dataScopePointCut()")
    public void clearThreadLocal() {
        threadLocal.remove();
        log.debug("threadLocal.remove()");
    }
}